package com.unidt.www.module.expert.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.unidt.www.framework.common.exception.util.ServiceExceptionUtil;
import com.unidt.www.framework.common.util.object.ConvertUtil;
import com.unidt.www.framework.common.util.string.RandomUtils;
import com.unidt.www.framework.security.core.LoginUser;
import com.unidt.www.framework.security.core.util.SecurityFrameworkUtils;
import com.unidt.www.module.expert.constant.RuleType;
import com.unidt.www.module.expert.domain.ExpertInvitationDo;
import com.unidt.www.module.expert.domain.ExpertInvitationRuleDo;
import com.unidt.www.module.expert.domain.ExpertRuleUserDo;
import com.unidt.www.module.expert.domain.ExpertShieldingConditionDo;
import com.unidt.www.module.expert.dto.LoadDataDto;
import com.unidt.www.module.expert.dto.RuleQueryDto;
import com.unidt.www.module.expert.form.*;
import com.unidt.www.module.expert.mapper.*;
import com.unidt.www.module.expert.service.InvitationDataService;
import com.unidt.www.module.expert.vo.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * @Author sk
 * @Description:
 * @Date 2022/12/15 16:38
 **/
@Service
public class InvitationDataServiceImpl implements InvitationDataService {

    @Autowired
    ExpertInfoMapper expertInfoMapper;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    ExpertInvitationMapper expertInvitationMapper;

    @Autowired
    ExpertShieldingConditionMapper expertShieldingConditionMapper;

    @Autowired
    ExpertInvitationRuleMapper expertInvitationRuleMapper;

    @Autowired
    ExpertRuleUserMapper expertRuleUserMapper;

    @Override
    public ExpertInfoUserDataVo load(InvitationRuleLoadForm loadForm) {
        ExpertInfoUserDataVo expertInfoUserDataVo = new
                ExpertInfoUserDataVo();
        List<InvitationRuleForm> rules = loadForm.getRules();
        List<ExpertRuleLoadResultVo> loadResultVos = ConvertUtil.convertList(rules, ExpertRuleLoadResultVo.class);
        List<ExpertUserVo> expertList = new CopyOnWriteArrayList<>();


        if (rules != null && !rules.isEmpty()) {
            List<ExpertUserVo> finalExpertList = expertList;
            rules.forEach(r -> {
                /**
                 * 封装查询条件
                 */
                RuleQueryDto ruleQueryDto = this.createQueryDto(r);
                List<ExpertUserVo> list = this.expertInfoMapper.selectListByRule(ruleQueryDto);
                list.forEach(e -> {
                    e.setRuleName(r.getName());
                });
                finalExpertList.addAll(list);
            });
        }
        ExpertShieldingConditionForm conditionForm = loadForm.getConditions();

        expertList = expertList.stream()
                .collect(Collectors.collectingAndThen(Collectors.toCollection(
                        () -> new TreeSet<>(Comparator.comparing(ExpertUserVo::getExpertId))), ArrayList::new));
        /**
         * 转换成VO
         */
        List<ExpertRuleVo> expertRuleVos = ConvertUtil.convertList(rules, ExpertRuleVo.class);

        expertRuleVos.forEach(e -> {
            /**
             * 设置规则类型为随机
             */
            e.setType(RuleType.RANDOM);
        });

        ShieldingConditionVo expertShieldingConditionVo = null;
        if (conditionForm != null) {
            /**
             * 转换条件
             */
            expertShieldingConditionVo = ConvertUtil.convert(conditionForm, ShieldingConditionVo.class);

            /**
             * 屏蔽专家姓名
             * 单位名称
             *
             */
            List<String> shieldingNames = conditionForm.shieldingNames();
            List<String> shieldingUnitNames = conditionForm.shieldingUnitNames();
            Boolean isUnitLimit = conditionForm.getIsUnitLimit();

            List<ExpertUserVo> expertRuleUserVoList = expertList;
            expertList.forEach(e -> {

                String expertName = e.getName();
                if (shieldingNames.contains(expertName)) {
                    expertRuleUserVoList.remove(e);
                }
                String unitName = e.getUnitName();
                if (shieldingUnitNames.contains(unitName)) {
                    expertRuleUserVoList.remove(e);
                }
            });

            /**
             * 单位去重
             */
            if (isUnitLimit) {
                expertList = expertList.stream().collect(Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getUnitName()))), ArrayList::new));
            }
        }


        /**
         * 计算结果
         */
        Map<String, List<ExpertUserVo>> expertMap = new HashMap<>();

        if (!expertList.isEmpty()) {
            expertMap = expertList.stream().collect(Collectors.groupingBy(ExpertUserVo::getRuleName));
        }

        /**
         * 遍历规则
         */
        Map<String, List<ExpertUserVo>> finalExpertMap = expertMap;
        loadResultVos.forEach(r -> {
            String ruleName = r.getName();
            Integer inviteesNumbers = r.getInviteesNumbers();
            Integer totalCount = 0;
            List<ExpertUserVo> ruleUserVoList = finalExpertMap.get(ruleName);

            if (ruleUserVoList != null) {
                totalCount = ruleUserVoList.size();
            }
            r.setTotalCount(totalCount);
            String messageTmp = "已检索到满足条件人数共{0}名,{1}计划邀约人数{2}人";
            if (totalCount > inviteesNumbers) {
                r.setLoadStatus(1);
                String message = MessageFormat.format(messageTmp,
                        new String[]{String.valueOf(totalCount), "大于", String.valueOf(inviteesNumbers)});
                r.setLoadMessage(message);
            } else if (totalCount.equals(inviteesNumbers)) {
                r.setLoadStatus(1);
                String message = MessageFormat.format(messageTmp,
                        new String[]{String.valueOf(totalCount), "等于", String.valueOf(inviteesNumbers)});
                r.setLoadMessage(message);
            } else {
                r.setLoadStatus(0);
                String message = MessageFormat.format(messageTmp,
                        new String[]{String.valueOf(totalCount), "小于", String.valueOf(inviteesNumbers)});
                r.setLoadMessage(message);
            }

        });
        expertInfoUserDataVo.setLoadResultVoList(loadResultVos);
        expertInfoUserDataVo.setExpertUserList(expertList);


        /**
         * 缓存对象 loadKey
         *  条件
         *  规则
         *  专家列表
         */
        LoadDataDto loadDataDto = new LoadDataDto();
        loadDataDto.setCondition(expertShieldingConditionVo);
        loadDataDto.setExpertRuleUsers(expertList);
        loadDataDto.setRules(expertRuleVos);

        String loadKey = RandomUtils.getMixString(16);
        loadDataDto.setLoadKey(loadKey);
        loadDataDto.setRuleType(RuleType.RANDOM);

        expertInfoUserDataVo.setLoadKey(loadKey);
        this.saveCache(loadDataDto);

        return expertInfoUserDataVo;
    }

    private void saveCache(LoadDataDto loadDataDto) {

        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();

        String loadKey = loadDataDto.getLoadKey();
        String cache_key = "EXPERT:" + loginUser.getId() + ":" + loadKey;
        loadDataDto.setLoadKey(loadKey);
        this.redisTemplate.opsForValue().set(cache_key,
                JSON.toJSONString(loadDataDto), 30, TimeUnit.MINUTES);
    }

    /**
     * 封装动态查询条件
     *
     * @param form
     * @return
     */
    private RuleQueryDto createQueryDto(InvitationRuleForm form) {

        RuleQueryDto ruleQueryDto = new RuleQueryDto();
        if (StringUtils.isNotBlank(form.getOrganIds())) {
            String[] organIds = form.getOrganIds().split(",");
            ruleQueryDto.setOrganIds(organIds);
        }
        if (StringUtils.isNotBlank(form.getAreaExpertises())) {
            String[] areaExpertises = form.getAreaExpertises().split(",");
            ruleQueryDto.setAreaExpertises(areaExpertises);

        }
        if (StringUtils.isNotBlank(form.getTags())) {
            String[] tags = form.getTags().split(",");
            ruleQueryDto.setTags(tags);
        }
        return ruleQueryDto;
    }


    /**
     * 功能描述: 定向查询条件
     *
     * @param form
     * @return com.unidt.www.module.expert.dto.RuleQueryDto
     * @author sk
     * @date 2022/12/19
     */
    private RuleQueryDto createQueryDto(DirectionalRuleForm form) {
        RuleQueryDto ruleQueryDto = new RuleQueryDto();
        if (StringUtils.isNotBlank(form.getOrganIds())) {
            String[] organIds = form.getOrganIds().split(",");
            ruleQueryDto.setOrganIds(organIds);
        }
        if (StringUtils.isNotBlank(form.getSearchValue())) {
            ruleQueryDto.setSearchValue(form.getSearchValue());
        }

        return ruleQueryDto;
    }


    @Override
    public ExpertListVo loadByKey(String key) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();

        String cache_key = "EXPERT:" + loginUser.getId() + ":" + key;
        String json = redisTemplate.opsForValue().get(cache_key);
        if (StringUtils.isNotBlank(json)) {
            LoadDataDto loadDataDto = JSON.parseObject(json, LoadDataDto.class);
            List<ExpertRuleVo> rules = loadDataDto.getRules();
            Integer inviteesNumbers = 0;
            if (rules != null && !rules.isEmpty()) {
                for (ExpertRuleVo ruleVo : rules) {
                    inviteesNumbers += ruleVo.getInviteesNumbers();
                }
            }


            List list = loadDataDto.getExpertRuleUsers();
            Integer total = list.size();
            ExpertListVo expertListVo = new ExpertListVo(total, inviteesNumbers, list);

            return expertListVo;
        }

        return new ExpertListVo();
    }

    @Override
    public Boolean saveExperts(ExpertInfoUserDataForm directionalRuleForm) {



        String loadKey = directionalRuleForm.getLoadKey();

        LoadDataDto loadDataDto = getFromCache(loadKey);
        if (Objects.nonNull(loadDataDto)) {
            List<ExpertRuleVo> rules = loadDataDto.getRules();
            /**
             * 更新规则
             */
            rules.forEach(r -> {
                r.setName(directionalRuleForm.getName());
                r.setInviteesNumbers(directionalRuleForm.getInviteesNumbers());
            });
            List<ExpertRuleUserFrom> expertList = directionalRuleForm.getExpertList();

            List<ExpertUserVo> expertRuleUsers = ConvertUtil.convertList(expertList, ExpertUserVo.class);

            /**
             * 定向选择
             * 规则只有一个
             */
            expertRuleUsers.forEach(e->{
                e.setRuleName(directionalRuleForm.getName());
            });
            loadDataDto.setExpertRuleUsers(expertRuleUsers);
            this.saveCache(loadDataDto);

        }
        return true;
    }

    @Override
    public Boolean deleteExpert(String loadKey, Long expertId) {
        LoadDataDto loadDataDto = getFromCache(loadKey);
        if (Objects.nonNull(loadDataDto)) {
            List<ExpertUserVo> list = loadDataDto.getExpertRuleUsers();
            List<ExpertUserVo> newExperts = new CopyOnWriteArrayList<>();
            list.forEach(l -> {
                Long id = l.getExpertId();
                if (!expertId.equals(id)) {
                    newExperts.add(l);
                    return;
                }

            });
            loadDataDto.setExpertRuleUsers(newExperts);
            this.updateCache(loadDataDto);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean save(ExpertInvitationForm invitationForm) {

        LoadDataDto loadDataDto = this.getFromCache(invitationForm.getLoadKey());
        if (Objects.nonNull(loadDataDto)) {
            ExpertInvitationDo expertInvitationDo = ConvertUtil.convert(invitationForm, ExpertInvitationDo.class);
            expertInvitationDo.setRuleType(loadDataDto.getRuleType());
            expertInvitationMapper.insert(expertInvitationDo);
            Long invitationId = expertInvitationDo.getId();
            /**
             * 1 保存过滤条件
             */
            ShieldingConditionVo conditionVo = loadDataDto.getCondition();
            if (Objects.nonNull(conditionVo)) {
                ExpertShieldingConditionDo expertShieldingCondition =
                        ConvertUtil.convert(conditionVo, ExpertShieldingConditionDo.class);
                expertShieldingCondition.setInvitationId(invitationId);
                this.expertShieldingConditionMapper.insert(expertShieldingCondition);
            }

            /**
             * 2 保存筛选规则
             */

            List<ExpertRuleVo> ruleVoList = loadDataDto.getRules();
            if (ruleVoList != null && !ruleVoList.isEmpty()) {
                List<ExpertInvitationRuleDo> ruleDos = ConvertUtil.convertList(ruleVoList, ExpertInvitationRuleDo.class);
                ruleDos.forEach(r -> {
                    r.setInvitationId(invitationId);
                });
                this.expertInvitationRuleMapper.insertBatch(ruleDos);

                Map<String, ExpertInvitationRuleDo> expertMap = ruleDos.stream().collect(Collectors.toMap(ExpertInvitationRuleDo::getName, a -> a));

                /**
                 * 3 保存专家列表
                 * 先分组 在保存
                 */
                List<ExpertUserVo> expertRuleUsers = loadDataDto.getExpertRuleUsers();
                expertRuleUsers.forEach(e -> {
                    ExpertRuleUserDo expertRuleUserDo = ConvertUtil.convert(e, ExpertRuleUserDo.class);
                    expertRuleUserDo.setInvitationId(invitationId);
                    ExpertInvitationRuleDo ruleDo = expertMap.get(e.getRuleName());
                    if (Objects.nonNull(ruleDo)) {
                        expertRuleUserDo.setRuleId(ruleDo.getId());
                        expertRuleUserMapper.insert(expertRuleUserDo);
                    }
                });
            }

            /**
             * 删除缓存
             */
            this.clearCache(invitationForm.getLoadKey());
            return true;

        } else {
            throw ServiceExceptionUtil.exception0(-1, "邀约已保存");
        }
    }

    @Override
    public Boolean cancel(ExpertCancelForm expertCancelForm) {

        ExpertRuleUserDo expertRuleUserDo = this.expertRuleUserMapper.selectById(expertCancelForm.getId());
        if(Objects.nonNull(expertCancelForm)){

            /**
             * 获取邀约信息
             */
            ExpertInvitationDo expertInvitationDo =
                    this.expertInvitationMapper.selectById(expertRuleUserDo.getInvitationId());

            /**
             * 邀约名单状态验证
             */
            if(Integer.valueOf(1).equals(expertInvitationDo.getRosterStatus())){
                throw ServiceExceptionUtil.exception0(-1, "邀约名单已经确认不能删除");
            }

            Long  replaceExpertId = expertCancelForm.getReplaceExpertId();
            Integer replaceType =  expertCancelForm.getReplaceType();
            Long ruleId  = expertRuleUserDo.getRuleId();

            expertRuleUserDo.setCancelType(expertCancelForm.getCancelType());
            expertRuleUserDo.setReason(expertCancelForm.getReason());
            expertRuleUserDo.setReplaceType(replaceType);
            expertRuleUserDo.setStatus(2);
            ExpertRuleUserDo replaceExpert = null;

            if(Integer.valueOf(2).equals(replaceType)){
                /**
                 * 指定
                 */
                replaceExpert =   this.expertRuleUserMapper.getByRuleIdAndExpertId(ruleId,replaceExpertId);
            }else {
                /**
                 * 随机
                 */
                List<ExpertRuleUserDo> leftExperts = expertRuleUserMapper.getLeftList(ruleId);
                if(!leftExperts.isEmpty()){
                    replaceExpert = leftExperts.get(0);
                }
            }

            if(Objects.isNull(replaceExpert)){
                throw ServiceExceptionUtil.exception0(-1, "备选专家不存在或没有更多的备选专家");
            }else {
                replaceExpert.setIsReplace(true);
                expertRuleUserDo.setReplaceExpertId(replaceExpert.getExpertId());
                this.expertRuleUserMapper.updateById(replaceExpert);

            }
            /**
             * 更新当前专家
             */
            this.expertRuleUserMapper.updateById(expertRuleUserDo);

            return true;
        }
        return false;
    }

    @Override
    public Boolean confirm(Long id) {

        ExpertInvitationDo expertInvitationDo =
                expertInvitationMapper.selectById(id);

        if(Objects.nonNull(expertInvitationDo)){
              Integer rosterStatus= expertInvitationDo.getRosterStatus();
              if(Integer.valueOf(1).equals(rosterStatus)){
                  throw ServiceExceptionUtil.exception0(-1, "邀约名单已经确认 请勿重复确认");
              }else {
                  expertInvitationDo.setRosterStatus(1);
                  this.expertInvitationMapper.updateById(expertInvitationDo);
              }
        }else {
            throw ServiceExceptionUtil.exception0(-1, "邀约不存在");
        }
        return true;
    }

    @Override
    public Boolean start(Long id) {
        LambdaUpdateWrapper<ExpertInvitationDo> lambdaUpdateWrapper =
                new LambdaUpdateWrapper();
        lambdaUpdateWrapper.set(ExpertInvitationDo::getStatus, 1);
        lambdaUpdateWrapper.eq(ExpertInvitationDo::getId, id);
        int count = expertInvitationMapper.update(null, lambdaUpdateWrapper);
        if (count >= 1) {
            return true;
        }
        return false;
    }

    @Override
    public Boolean delete(Long id) {

        this.expertInvitationMapper.deleteById(id);

        return true;
    }


    @Override
    public ExpertInfoUserDataVo directionalLoad(DirectionalRuleForm loadForm) {
        ExpertInfoUserDataVo expertInfoUserDataVo = new ExpertInfoUserDataVo();

        /**
         * 创建查询条件
         */
        RuleQueryDto ruleQueryDto = this.createQueryDto(loadForm);
        List<ExpertUserVo> list = this.expertInfoMapper.selectListByRule(ruleQueryDto);


        List<ExpertUserVo> expertRuleVos = ConvertUtil.convertList(list, ExpertUserVo.class);
        expertInfoUserDataVo.setExpertUserList(expertRuleVos);

        ExpertRuleVo expertRuleVo = ConvertUtil.convert(loadForm, ExpertRuleVo.class);
        expertRuleVo.setType(RuleType.DIRECTION);
        List<ExpertRuleVo> ruleVos = new ArrayList<>();
        ruleVos.add(expertRuleVo);

        String loadKey = RandomUtils.getMixString(16);
        expertInfoUserDataVo.setLoadKey(loadKey);

        /**
         * 保存规则
         */
        LoadDataDto loadDataDto = new LoadDataDto();
        loadDataDto.setRuleType(RuleType.DIRECTION);
        loadDataDto.setRules(ruleVos);
        loadDataDto.setLoadKey(loadKey);

        this.saveCache(loadDataDto);
        return expertInfoUserDataVo;
    }

    /**
     * 功能描述: 从缓存中获取邀约信息数据
     * @author sk
     * @date 2023/1/3
     * @param loadKey
     * @return com.unidt.www.module.expert.dto.LoadDataDto
     */
    private LoadDataDto getFromCache(String loadKey) {

        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();

        String cache_key = "EXPERT:" + loginUser.getId() + ":" + loadKey;
        String json = redisTemplate.opsForValue().get(cache_key);
        if (StringUtils.isNotBlank(json)) {
            LoadDataDto loadDataDto = JSON.parseObject(json, LoadDataDto.class);
            return loadDataDto;
        } else {
            return null;
        }
    }

    /**
     * 功能描述: 更新缓存
     * @author sk
     * @date 2023/1/3
     * @param loadDataDto
     * @return void
     */
    private void updateCache(LoadDataDto loadDataDto) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        String cache_key = "EXPERT:" + loginUser.getId() + ":" + loadDataDto.getLoadKey();
        this.redisTemplate.opsForValue().set(cache_key, JSON.toJSONString(loadDataDto), 30, TimeUnit.MINUTES);
    }

    /**
     * 功能描述: 清除缓存
     * @author sk
     * @date 2023/1/3
     * @param loadKey
     * @return void
     */
    private void clearCache(String loadKey) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        String cache_key = "EXPERT:" + loginUser.getId() + ":" + loadKey;
        this.redisTemplate.delete(cache_key);

    }


}
